﻿/****************************************************************************************************
	Copyright (C) 2010 RapidWebDev Organization (http://rapidwebdev.org)
	Author: Eunge, Legal Name: Jian Liu, Email: eunge.liu@RapidWebDev.org

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ****************************************************************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web.UI;
using System.Web.UI.WebControls;
using BaoJianSoft.Common;
using BaoJianSoft.Common.Validation;
using BaoJianSoft.Common.Web;
using BaoJianSoft.ExtensionModel.Statistic;
using BaoJianSoft.ExtensionModel.Web.Properties;
using BaoJianSoft.RapidWeb.DynamicPages;
using MyControls = BaoJianSoft.RapidWeb.Controls;

namespace BaoJianSoft.ExtensionModel.Web.DynamicPage
{
	/// <summary>
	/// Detail panel to CRUD statistic field for special statistic report.
	/// The class is designed to manage content of the statistic report provided by implementation classes. 
	/// So the only works needs to do for implementation classes are 1) store and resolve statistic report somewhere and 2) provide a list of statistic report candidate properties.
	/// </summary>
	public abstract class StatisticFieldDetailPanel : DetailPanelPage
	{
		protected static IStatisticApi statisticApi = SpringContext.Current.GetObject<IStatisticApi>();

		#region Binding Web Controls

		[Binding]
		protected DropDownList ComboBoxStatisticField;

		[Binding]
		protected TextBox TextBoxHeaderText;
		[Binding]
		protected RadioButtonList RadioButtonListLocation;
		[Binding]
		protected MyControls.IntegerTextBox TextBoxOrdinal;

		[Binding]
		protected DropDownList ComboBoxStringAlgorithm;
		[Binding]
		protected TextBox TextBoxStringValue;

		[Binding]
		protected MyControls.DatePicker DatePickerStart;
		[Binding]
		protected MyControls.DatePicker DatePickerEnd;

		[Binding]
		protected MyControls.IntegerTextBox IntegerTextBoxMin;
		[Binding]
		protected MyControls.IntegerTextBox IntegerTextBoxMax;

		[Binding]
		protected MyControls.DecimalTextBox DecimalTextBoxMin;
		[Binding]
		protected MyControls.DecimalTextBox DecimalTextBoxMax;

		[Binding]
		protected MyControls.ComboBox ComboBoxSelection;

		[Binding]
		protected CheckBoxList CheckBoxListAggregateColumns;

		#endregion

		/// <summary>
		/// Create a statistic property.
		/// </summary>
		/// <returns></returns>
		public override string Create()
		{
			this.ValidateInputData(null);

			CandidateStatisticProperty property = this.ResolveCandidateStatisticPropertyById(this.ComboBoxStatisticField.SelectedValue);

			int ordinal = this.TextBoxOrdinal.Value.HasValue ? this.TextBoxOrdinal.Value.Value : 999;
			StatisticFilter statisticFilter = statisticApi.TransformToStatisticFilter(property, this.TextBoxHeaderText.Text, ordinal);
			this.SetStatisticFilterItemFromControls(statisticFilter, property);

			List<StatisticFilter> statisticFilters = new List<StatisticFilter>();
			StatisticDefinition statisticDefinition = this.GetStatisticDefinition();
			StatisticFilterLocation location = (StatisticFilterLocation)Enum.Parse(typeof(StatisticFilterLocation), this.RadioButtonListLocation.SelectedValue);
			if (location == StatisticFilterLocation.Column)
			{
				if (statisticDefinition.Columns != null)
					statisticFilters = statisticDefinition.Columns.ToList();

				statisticFilters.Add(statisticFilter);
				statisticDefinition.Columns = statisticFilters.OrderBy(filter => filter.Ordinal).ToArray();
			}
			else if (location == StatisticFilterLocation.Row)
			{
				if (statisticDefinition.Rows != null)
					statisticFilters = statisticDefinition.Rows.ToList();

				statisticFilters.Add(statisticFilter);
				statisticDefinition.Rows = statisticFilters.OrderBy(filter => filter.Ordinal).ToArray();
			}

			this.SaveStatisticDefinition(statisticDefinition);
			return GenerateStatisticFilterId(location, this.TextBoxHeaderText.Text);
		}

		/// <summary>
		/// Update an existed statistic filter in the statistic definition.
		/// </summary>
		/// <param name="entityId"></param>
		public override void Update(string entityId)
		{
			this.ValidateInputData(entityId);

			CandidateStatisticProperty property = this.ResolveCandidateStatisticPropertyById(this.ComboBoxStatisticField.SelectedValue);

			int ordinal = this.TextBoxOrdinal.Value.HasValue ? this.TextBoxOrdinal.Value.Value : 999;
			StatisticFilter statisticFilter = statisticApi.TransformToStatisticFilter(property, this.TextBoxHeaderText.Text, ordinal);
			this.SetStatisticFilterItemFromControls(statisticFilter, property);

			StatisticDefinition statisticDefinition = this.GetStatisticDefinition();
			StatisticFilterLocation location = (StatisticFilterLocation)Enum.Parse(typeof(StatisticFilterLocation), this.RadioButtonListLocation.SelectedValue);

			List<StatisticFilter> statisticFilters = new List<StatisticFilter>();
			if (statisticDefinition.Columns != null)
					statisticFilters = statisticDefinition.Columns.Where(filter => GenerateStatisticFilterId(StatisticFilterLocation.Column, filter.HeaderText) != entityId).ToList();
			if (location == StatisticFilterLocation.Column)
				statisticFilters.Add(statisticFilter);
			statisticDefinition.Columns = statisticFilters.OrderBy(filter => filter.Ordinal).ToArray();

			statisticFilters = new List<StatisticFilter>();
			if (statisticDefinition.Rows != null)
				statisticFilters = statisticDefinition.Rows.Where(filter => GenerateStatisticFilterId(StatisticFilterLocation.Row, filter.HeaderText) != entityId).ToList();
			if (location == StatisticFilterLocation.Row)
				statisticFilters.Add(statisticFilter);
			statisticDefinition.Rows = statisticFilters.OrderBy(filter => filter.Ordinal).ToArray();

			this.SaveStatisticDefinition(statisticDefinition);
		}

		/// <summary>
		/// Load special statistic filter to edit.
		/// </summary>
		/// <param name="entityId"></param>
		public override void LoadWritableEntity(string entityId)
		{
			StatisticDefinition statisticDefinition = this.GetStatisticDefinition();
			StatisticFilter statisticFilter = null;

			this.ComboBoxStatisticField.Enabled = false;

			if (statisticDefinition.Columns != null)
			{
				statisticFilter = statisticDefinition.Columns.FirstOrDefault(filter => GenerateStatisticFilterId(StatisticFilterLocation.Column, filter.HeaderText) == entityId);
				this.RadioButtonListLocation.SelectedValue = StatisticFilterLocation.Column.ToString();
			}

			if (statisticFilter == null && statisticDefinition.Rows != null)
			{
				statisticFilter = statisticDefinition.Rows.FirstOrDefault(filter => GenerateStatisticFilterId(StatisticFilterLocation.Row, filter.HeaderText) == entityId);
				this.RadioButtonListLocation.SelectedValue = StatisticFilterLocation.Row.ToString();
			}

			string candidateStatisticPropertyId = GenerateCandidateStatisticPropertyId(statisticFilter.PropertyLabel);
			CandidateStatisticProperty candidateStatisticProperty = this.ResolveCandidateStatisticPropertyById(candidateStatisticPropertyId);

			this.ComboBoxStatisticField.SelectedValue = candidateStatisticPropertyId;
			this.SetStatisticFieldFormVisibility();

			this.TextBoxHeaderText.Text = statisticFilter.HeaderText;
			this.TextBoxOrdinal.Value = statisticFilter.Ordinal;

			this.SetControlsFromStatisticFilter(statisticFilter, candidateStatisticProperty);
		}

		/// <summary>
		/// Load all candidate statistic properties.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public override void OnLoad(object sender, DetailPanelPageEventArgs e)
		{
			this.SetStatisticFieldFormVisibility();

			Page webpage = sender as Page;
			if (!webpage.IsPostBack)
			{
				// add statistic fields
				this.ComboBoxStatisticField.Items.Clear();
				this.ComboBoxStatisticField.Items.Add(new ListItem("", ""));
				IEnumerable<CandidateStatisticProperty> candidateStatisticProperties = this.GetAllCandidateStatisticProperties();
				foreach (CandidateStatisticProperty candidateStatisticProperty in candidateStatisticProperties)
				{
					string itemValue = GenerateCandidateStatisticPropertyId(candidateStatisticProperty.PropertyLabel);
					ListItem candidateStatisticPropertyItem = new ListItem(candidateStatisticProperty.PropertyLabel, itemValue);
					candidateStatisticPropertyItem.Attributes["ControlType"] = GetConfigureControlType(candidateStatisticProperty).ToString();
					this.ComboBoxStatisticField.Items.Add(candidateStatisticPropertyItem);
				}

				// add aggregate target filters
				this.CheckBoxListAggregateColumns.Items.Clear();
				StatisticDefinition statisticDefinition = this.GetStatisticDefinition();
				if (statisticDefinition.Columns != null)
				{
					IEnumerable<StatisticFilter> columnStatisticFilters = from filter in statisticDefinition.Columns
																		  where !(filter is AggregateStatisticFilter)
																		  select filter;
					foreach (StatisticFilter columnStatisticFilter in columnStatisticFilters)
						this.CheckBoxListAggregateColumns.Items.Add(new ListItem(columnStatisticFilter.HeaderText, columnStatisticFilter.HeaderText));
				}
			}
			else
			{
				IEnumerable<CandidateStatisticProperty> candidateStatisticProperties = this.GetAllCandidateStatisticProperties();
				foreach (ListItem listItem in this.ComboBoxStatisticField.Items)
				{
					CandidateStatisticProperty candidateStatisticProperty = candidateStatisticProperties.FirstOrDefault(p => GenerateCandidateStatisticPropertyId(p.PropertyLabel) == listItem.Value);
					if (candidateStatisticProperty != null)
						listItem.Attributes["ControlType"] = GetConfigureControlType(candidateStatisticProperty).ToString();
				}
			}
		}

		/// <summary>
		/// Set JavaScript variable "window.SelectionStatisticFilterComboBox" which references to the control ComboBoxSelection.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public override void OnPreRender(object sender, DetailPanelPageEventArgs e)
		{
			string javaScriptBlock = string.Format(CultureInfo.InvariantCulture, "window.SelectionStatisticFilterComboBox={0};", MyControls.ControlJavaScriptGenerator.GenerateVariableName(this.ComboBoxSelection.ClientID));
			ClientScripts.OnDocumentReady.Add2EndOfBody(javaScriptBlock, JavaScriptPriority.Normal);
		}

		/// <summary>
		/// Set controls from statistic filter.
		/// </summary>
		/// <param name="statisticFilter"></param>
		/// <param name="candidateStatisticProperty"></param>
		protected virtual void SetControlsFromStatisticFilter(StatisticFilter statisticFilter, CandidateStatisticProperty candidateStatisticProperty)
		{
			PropertyConfigurationControlType controlType = GetConfigureControlType(candidateStatisticProperty);

			BetweenStatisticFilter betweenStatisticFilter;
			EqualsStatisticFilter equalsStatisticFilter;
			ContainsStatisticFilter containsStatisticFilter;
			ContainedStatisticFilter containedStatisticFilter;
			switch (controlType)
			{
				#region DateTime

				case PropertyConfigurationControlType.DateTime:
					betweenStatisticFilter = statisticFilter as BetweenStatisticFilter;
					DateTime dateTimeMinValue;
					if (DateTime.TryParse(betweenStatisticFilter.MinValue, out dateTimeMinValue))
						this.DatePickerStart.SelectedValue = dateTimeMinValue;

					DateTime dateTimeMaxValue;
					if (DateTime.TryParse(betweenStatisticFilter.MaxValue, out dateTimeMaxValue))
						this.DatePickerEnd.SelectedValue = dateTimeMaxValue.AddDays(-1);

					return;

				#endregion

				#region Decimal

				case PropertyConfigurationControlType.Decimal:
					betweenStatisticFilter = statisticFilter as BetweenStatisticFilter;
					decimal decimalMinValue;
					if (decimal.TryParse(betweenStatisticFilter.MinValue, out decimalMinValue))
						this.DecimalTextBoxMin.Value = decimalMinValue;

					decimal decimalMaxValue;
					if (decimal.TryParse(betweenStatisticFilter.MaxValue, out decimalMaxValue))
						this.DecimalTextBoxMax.Value = decimalMaxValue;

					return;

				#endregion

				#region Integer

				case PropertyConfigurationControlType.Integer:
					betweenStatisticFilter = statisticFilter as BetweenStatisticFilter;
					int intMinValue;
					if (int.TryParse(betweenStatisticFilter.MinValue, out intMinValue))
						this.IntegerTextBoxMin.Value = intMinValue;

					int intMaxValue;
					if (int.TryParse(betweenStatisticFilter.MaxValue, out intMaxValue))
						this.IntegerTextBoxMax.Value = intMaxValue;

					return;

				#endregion

				#region String

				case PropertyConfigurationControlType.String:
					if (statisticFilter is EqualsStatisticFilter)
					{
						equalsStatisticFilter = statisticFilter as EqualsStatisticFilter;
						this.TextBoxStringValue.Text = equalsStatisticFilter.Item.Value;
					}
					else if (statisticFilter is ContainsStatisticFilter)
					{
						containsStatisticFilter = statisticFilter as ContainsStatisticFilter;
						this.TextBoxStringValue.Text = containsStatisticFilter.Item.Value;
					}
					else if (statisticFilter is ContainedStatisticFilter)
					{
						containedStatisticFilter = statisticFilter as ContainedStatisticFilter;
						this.TextBoxStringValue.Text = containedStatisticFilter.Item != null && containedStatisticFilter.Item.Length > 0 ? containedStatisticFilter.Item[0].Value : "";
					}

					return;

				#endregion

				#region Aggregate

				case PropertyConfigurationControlType.Aggregate:
					AggregateStatisticFilter aggregateStatisticFilter = statisticFilter as AggregateStatisticFilter;
					if (aggregateStatisticFilter == null || aggregateStatisticFilter.ReferencedFilter == null || this.CheckBoxListAggregateColumns == null)
						return;

					IEnumerable<string> aggregateReferencedFilters = aggregateStatisticFilter.ReferencedFilter.Select(refFilter=>refFilter.HeaderText);
					foreach (ListItem aggregateColumn in this.CheckBoxListAggregateColumns.Items)
						aggregateColumn.Selected = aggregateReferencedFilters.Contains(aggregateColumn.Value);

					return;

				#endregion

				#region SingleSelection

				case PropertyConfigurationControlType.SingleSelection:
					StatisticItem statisticItem = candidateStatisticProperty.Item.GetItems().FirstOrDefault(item => item.Value == GenerateCandidateStatisticPropertyId(candidateStatisticProperty.PropertyLabel));
					if (statisticFilter is EqualsStatisticFilter)
					{
						equalsStatisticFilter = statisticFilter as EqualsStatisticFilter;
						this.ComboBoxSelection.Items.Clear();
						this.ComboBoxSelection.Items.Add(new ListItem(equalsStatisticFilter.Item.Text, equalsStatisticFilter.Item.Value));
						this.ComboBoxSelection.SelectedValue = equalsStatisticFilter.Item.Value;
						this.ComboBoxSelection.SelectedText = equalsStatisticFilter.Item.Text;
					}
					else if (statisticFilter is ContainsStatisticFilter)
					{
						containsStatisticFilter = statisticFilter as ContainsStatisticFilter;
						this.ComboBoxSelection.Items.Clear();
						this.ComboBoxSelection.Items.Add(new ListItem(containsStatisticFilter.Item.Text, containsStatisticFilter.Item.Value));
						this.ComboBoxSelection.SelectedValue = containsStatisticFilter.Item.Value;
						this.ComboBoxSelection.SelectedText = containsStatisticFilter.Item.Text;
					}
					else if (statisticFilter is ContainedStatisticFilter)
					{
						containedStatisticFilter = statisticFilter as ContainedStatisticFilter;
						if (containedStatisticFilter.Item != null && containedStatisticFilter.Item.Length > 0)
						{
							this.ComboBoxSelection.Items.Clear();
							this.ComboBoxSelection.Items.Add(new ListItem(containedStatisticFilter.Item[0].Text, containedStatisticFilter.Item[0].Value));
							this.ComboBoxSelection.SelectedValue = containedStatisticFilter.Item[0].Value;
							this.ComboBoxSelection.SelectedText = containedStatisticFilter.Item[0].Text;
						}
					}
					else if (statisticFilter is CallbackStatisticFilter)
					{
						CallbackStatisticFilter callbackStatisticFilter = statisticFilter as CallbackStatisticFilter;
						if (callbackStatisticFilter.Item != null && callbackStatisticFilter.Item.Length > 0)
						{
							this.ComboBoxSelection.Items.Clear();
							this.ComboBoxSelection.Items.Add(new ListItem(callbackStatisticFilter.Item[0].Text, callbackStatisticFilter.Item[0].Value));
							this.ComboBoxSelection.SelectedValue = callbackStatisticFilter.Item[0].Value;
							this.ComboBoxSelection.SelectedText = callbackStatisticFilter.Item[0].Text;
						}
					}
					return;

				#endregion
			}
		}

		/// <summary>
		/// Set items of statistic filter from controls.
		/// </summary>
		/// <param name="statisticFilter"></param>
		/// <param name="property"></param>
		protected virtual void SetStatisticFilterItemFromControls(StatisticFilter statisticFilter, CandidateStatisticProperty property)
		{
			PropertyConfigurationControlType controlType = GetConfigureControlType(property);
			BetweenStatisticFilter betweenStatisticFilter;
			EqualsStatisticFilter equalsStatisticFilter;
			ContainsStatisticFilter containsStatisticFilter;
			ContainedStatisticFilter containedStatisticFilter;

			switch (controlType)
			{
				#region DateTime

				case PropertyConfigurationControlType.DateTime:
					betweenStatisticFilter = statisticFilter as BetweenStatisticFilter;
					if (this.DatePickerStart.SelectedValue.HasValue)
						betweenStatisticFilter.MinValue = this.DatePickerStart.SelectedValue.ToString();
					if (this.DatePickerEnd.SelectedValue.HasValue)
						betweenStatisticFilter.MaxValue = this.DatePickerEnd.SelectedValue.Value.AddDays(1).ToString();
					return;

				#endregion

				#region Decimal

				case PropertyConfigurationControlType.Decimal:
					betweenStatisticFilter = statisticFilter as BetweenStatisticFilter;
					if (this.DecimalTextBoxMin.Value.HasValue)
						betweenStatisticFilter.MinValue = this.DecimalTextBoxMin.Value.ToString();
					if (this.DecimalTextBoxMax.Value.HasValue)
						betweenStatisticFilter.MaxValue = this.DecimalTextBoxMax.Value.ToString();
					return;
				#endregion

				#region Integer

				case PropertyConfigurationControlType.Integer:
					betweenStatisticFilter = statisticFilter as BetweenStatisticFilter;
					if (this.IntegerTextBoxMin.Value.HasValue)
						betweenStatisticFilter.MinValue = this.IntegerTextBoxMin.Value.ToString();
					if (this.IntegerTextBoxMax.Value.HasValue)
						betweenStatisticFilter.MaxValue = this.IntegerTextBoxMax.Value.ToString();
					return;

				#endregion

				#region String

				case PropertyConfigurationControlType.String:
					if (statisticFilter is EqualsStatisticFilter)
					{
						equalsStatisticFilter = statisticFilter as EqualsStatisticFilter;
						equalsStatisticFilter.Item = new StatisticItem { Text = this.TextBoxStringValue.Text, Value = this.TextBoxStringValue.Text };
					}
					else if (statisticFilter is ContainsStatisticFilter)
					{
						containsStatisticFilter = statisticFilter as ContainsStatisticFilter;
						containsStatisticFilter.Item = new StatisticItem { Text = this.TextBoxStringValue.Text, Value = this.TextBoxStringValue.Text };
					}
					else if (statisticFilter is ContainedStatisticFilter)
					{
						containedStatisticFilter = statisticFilter as ContainedStatisticFilter;
						containedStatisticFilter.Item = new[] { new StatisticItem { Text = this.TextBoxStringValue.Text, Value = this.TextBoxStringValue.Text } };
					}
					return;

				#endregion

				#region Aggregate

				case PropertyConfigurationControlType.Aggregate:
					AggregateStatisticFilter aggregateStatisticFilter = statisticFilter as AggregateStatisticFilter;
					if (this.CheckBoxListAggregateColumns == null)
						throw new ValidationException(Resources.NoneAggregateReferencedStatisticFilter);

					List<AggregateReferencedFilter> aggregateReferencedFilters = new List<AggregateReferencedFilter>();
					foreach (ListItem aggregateColumn in this.CheckBoxListAggregateColumns.Items)
						if (aggregateColumn.Selected) aggregateReferencedFilters.Add(new AggregateReferencedFilter { HeaderText = aggregateColumn.Value });

					if (aggregateReferencedFilters.Count == 0)
						throw new ValidationException(Resources.NoneAggregateReferencedStatisticFilter);

					aggregateStatisticFilter.ReferencedFilter = aggregateReferencedFilters.ToArray();
					return;

				#endregion

				#region SingleSelection

				case PropertyConfigurationControlType.SingleSelection:
					StatisticItem statisticItem = property.Item.GetItems().FirstOrDefault(item => item.Value == this.ComboBoxSelection.SelectedValue);
					if (statisticFilter is EqualsStatisticFilter)
					{
						equalsStatisticFilter = statisticFilter as EqualsStatisticFilter;
						equalsStatisticFilter.Item = new StatisticItem { Text = statisticItem.Text, Value = statisticItem.Value };
					}
					else if (statisticFilter is ContainsStatisticFilter)
					{
						containsStatisticFilter = statisticFilter as ContainsStatisticFilter;
						containsStatisticFilter.Item = new StatisticItem { Text = statisticItem.Text, Value = statisticItem.Value };
					}
					else if (statisticFilter is ContainedStatisticFilter)
					{
						containedStatisticFilter = statisticFilter as ContainedStatisticFilter;
						containedStatisticFilter.Item = new[] { new StatisticItem { Text = statisticItem.Text, Value = statisticItem.Value } };
					}
					else if (statisticFilter is CallbackStatisticFilter)
					{
						CallbackStatisticFilter callbackStatisticFilter = statisticFilter as CallbackStatisticFilter;
						callbackStatisticFilter.Item = new[] { new StatisticItem { Text = statisticItem.Text, Value = statisticItem.Value } };
					}
					return;

				#endregion
			}
		}

		/// <summary>
		/// Validate input data submitted from client.
		/// </summary>
		protected virtual void ValidateInputData(string statisticFilterId)
		{
			try
			{
				using (ValidationScope validationScope = new ValidationScope())
				{
					if (Kit.IsEmpty(this.ComboBoxStatisticField.SelectedValue))
						validationScope.Error(Resources.PleaseSelectStatisticField);

					if (Kit.IsEmpty(this.TextBoxHeaderText.Text))
						validationScope.Error(Resources.StatisticHeaderTextCannotBeEmpty);

					StatisticDefinition statisticDefinition = this.GetStatisticDefinition();
					StatisticFilterLocation location = (StatisticFilterLocation)Enum.Parse(typeof(StatisticFilterLocation), this.RadioButtonListLocation.SelectedValue);
					if (location == StatisticFilterLocation.Column)
					{
						if (statisticDefinition.Columns != null && statisticDefinition.Columns.Count(filter => filter.HeaderText == this.TextBoxHeaderText.Text.Trim() && GenerateStatisticFilterId(StatisticFilterLocation.Column, filter.HeaderText) != statisticFilterId) > 0)
							validationScope.Error(Resources.DuplicateStatisticFilterHeaderTextInColumn);
					}
					else if (location == StatisticFilterLocation.Row)
					{
						if (statisticDefinition.Rows != null && statisticDefinition.Rows.Count(filter => filter.HeaderText == this.TextBoxHeaderText.Text.Trim() && GenerateStatisticFilterId(StatisticFilterLocation.Row, filter.HeaderText) != statisticFilterId) > 0)
							validationScope.Error(Resources.DuplicateStatisticFilterHeaderTextInRow);
					}

					CandidateStatisticProperty candidateStatisticProperty = this.ResolveCandidateStatisticPropertyById(this.ComboBoxStatisticField.SelectedValue);
					if (candidateStatisticProperty == null)
						validationScope.Error(Resources.StatisticFieldExpired);

					switch (GetConfigureControlType(candidateStatisticProperty))
					{
						case PropertyConfigurationControlType.DateTime:
							if (!this.DatePickerStart.SelectedValue.HasValue && !this.DatePickerEnd.SelectedValue.HasValue)
								validationScope.Error(Resources.StatisticFieldDateTimeCannotBeEmpty);
							break;

						case PropertyConfigurationControlType.Decimal:
							if (!this.DecimalTextBoxMin.Value.HasValue && !this.DecimalTextBoxMax.Value.HasValue)
								validationScope.Error(Resources.StatisticFieldDecimalCannotBeEmpty);
							break;

						case PropertyConfigurationControlType.Integer:
							if (!this.IntegerTextBoxMin.Value.HasValue && !this.IntegerTextBoxMax.Value.HasValue)
								validationScope.Error(Resources.StatisticFieldIntegerCannotBeEmpty);
							break;

						case PropertyConfigurationControlType.String:
							if (Kit.IsEmpty(this.TextBoxStringValue.Text))
								validationScope.Error(Resources.StatisticFieldStringValueCannotBeEmpty);
							break;

						case PropertyConfigurationControlType.Aggregate:
							int selectedAggregateTargetColumns = 0;
							foreach (ListItem listItem in this.CheckBoxListAggregateColumns.Items)
								if (listItem.Selected) selectedAggregateTargetColumns++;

							if (selectedAggregateTargetColumns == 0)
								validationScope.Error(Resources.NoneAggregateReferencedStatisticFilter);
							break;

						case PropertyConfigurationControlType.SingleSelection:
							if (Kit.IsEmpty(this.ComboBoxSelection.SelectedValue))
								validationScope.Error(Resources.StatisticFieldSelectionCannotBeEmpty);
							break;
					}
				}
			}
			catch (ValidationException)
			{
				throw;
			}
			catch
			{
				this.ComboBoxSelection.SelectedText = "";
				this.ComboBoxSelection.SelectedValue = "";
				throw;
			}
		}

		/// <summary>
		/// Get all candidate statistic properties which are used to build statistic report.
		/// </summary>
		/// <returns></returns>
		protected abstract IEnumerable<CandidateStatisticProperty> GetAllCandidateStatisticProperties();

		/// <summary>
		/// Get statistic definition for adding filter in or editing the filter.
		/// </summary>
		/// <returns></returns>
		protected abstract StatisticDefinition GetStatisticDefinition();

		/// <summary>
		/// Save statistic definition.
		/// </summary>
		/// <param name="statisticDefinition"></param>
		protected abstract void SaveStatisticDefinition(StatisticDefinition statisticDefinition);

		/// <summary>
		/// Generate an identity for statistic filter.
		/// </summary>
		/// <param name="location"></param>
		/// <param name="headerText"></param>
		/// <returns></returns>
		public static string GenerateStatisticFilterId(StatisticFilterLocation location, string headerText)
		{
			return string.Format(CultureInfo.InvariantCulture, "{0}:{1}", location, headerText).GetHashCode().ToString();
		}

		private CandidateStatisticProperty ResolveCandidateStatisticPropertyById(string candidateStatisticPropertyId)
		{
			IEnumerable<CandidateStatisticProperty> allCandidateStatisticProperties = this.GetAllCandidateStatisticProperties();
			return allCandidateStatisticProperties.FirstOrDefault(p => GenerateCandidateStatisticPropertyId(p.PropertyLabel) == candidateStatisticPropertyId);
		}

		private void SetStatisticFieldFormVisibility()
		{
			string javaScriptBlock = string.Format(CultureInfo.InvariantCulture, "changeStatisticField('{0}');", this.ComboBoxStatisticField.SelectedValue);
			ClientScripts.OnDocumentReady.Add2EndOfBody(javaScriptBlock, JavaScriptPriority.Low);
		}

		/// <summary>
		/// Generate an identity for candidate statistic property by the hashcode of PropertyLabel.
		/// </summary>
		/// <param name="propertyLabel"></param>
		/// <returns></returns>
		public static string GenerateCandidateStatisticPropertyId(string propertyLabel)
		{
			return propertyLabel.GetHashCode().ToString();
		}

		/// <summary>
		/// The UI control type to configure the property.
		/// </summary>
		private static PropertyConfigurationControlType GetConfigureControlType(CandidateStatisticProperty property)
		{
			if (property.Algorithm != null && property.Algorithm.Type == StatisticFilterAlgorithms.Aggregate)
				return PropertyConfigurationControlType.Aggregate;

			switch (property.PropertyType)
			{
				case PropertyTypes.DateTime:
					return PropertyConfigurationControlType.DateTime;
				case PropertyTypes.Decimal:
					return PropertyConfigurationControlType.Decimal;
				case PropertyTypes.Integer:
					return PropertyConfigurationControlType.Integer;
				case PropertyTypes.String:
					if (property.Item == null) return PropertyConfigurationControlType.String;
					else return PropertyConfigurationControlType.SingleSelection;
				case PropertyTypes.Enumerable:
					return PropertyConfigurationControlType.SingleSelection;
			}

			throw new NotSupportedException();
		}

		private enum PropertyConfigurationControlType
		{
			String,
			DateTime,
			Integer,
			Decimal,
			SingleSelection,
			Aggregate
		}
	}
}

